package org.flashamaphone.voices.waveformFactories
{
	import flash.utils.ByteArray;
	
	import org.flashamaphone.buffer.SoundConstants;
	import org.flashamaphone.voices.envelope.Envelope;
	
	/** Generates byte array cycles of a particular type of waveform */
	public class WaveFactory implements IWaveFactory
	{
		public static const MULTIPLIER:Number = (2*Math.PI)/SoundConstants.SAMPLING_RATE;
		
		/** phase correction for octave transposition */
		protected var _octaveTransposition:int = 1;
		
		/** last sample index */
		protected var _lastSampleIndex:int = 0;
		
		/** 
		 * getter for last sample index - helpful for ensuring seamless stitchin of sounds
		 * 
		 * @return index
		 */
		public function get lastSampleIndex():int {
			return _lastSampleIndex;
		}
		
		/** 
		 * set octave transposition
		 * 
		 * @return index
		 */
		public function set octaveTransposition(value:int):void {
			_octaveTransposition = value;
		}
		
		/** 
		 * get a sample at a specific phase of a cycle
		 * 
		 * @return index
		 */
		public function sample(phase:Number):Number {
			return 0;
		}
		
		/** 
		 * get the byte array from a complete cycle
		 * 
		 * @param frequency
		 * @param number of samples
		 * @param offset
		 * @param volume
		 * @param envelope
		 * @return byte array of cycle
		 */
		public function cycle(freq:Number, length:int, offset:int = 0, volume:Number = 1, envelope:Envelope = null):ByteArray {
			var returnBytes:ByteArray = new ByteArray();
			
			if (envelope) {
				envelope.rate = (envelope.endValue - envelope.startValue)/length;
			}

			for (var i:int=offset; i < length + offset; i++ ) {
				var position:Number = i * WaveFactory.MULTIPLIER * freq;
				var sample:Number = sample(position);
				var enveloped:Number = sample;
				if (envelope) {
					enveloped = envelope.step(sample);
				}
				returnBytes.writeFloat(enveloped * volume);
				returnBytes.writeFloat(enveloped * volume);
			}
			_lastSampleIndex = i;
			return returnBytes;
		}
		
		/**
		 * phase correction
		 * 
		 * @param number of octaves
		 */
		protected function phaseCorrection(phase:Number):Number {
			if(_octaveTransposition > 0) {
				phase *= Math.pow(2 , Math.abs(_octaveTransposition));
			} else {
				phase /= Math.pow(2 , Math.abs(_octaveTransposition));
			}
			return phase;
		}
	}
}